package com.hzgj.bcl.soa.cicada.lb;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import com.hzgj.bcl.soa.cicada.sender.Sender;
import com.hzgj.bcl.soa.config.ReferenceDef;
import com.hzgj.bcl.soa.rpc.IpAddress;

import java.util.List;
import java.util.Map;

/**
 * Created by warning5 on 17-6-14.
 */
public class WeightedRoundRule extends AbstractRule {

    private Map<IpAddress, WeightedSender> configWeightedSenders;

    public WeightedRoundRule() {
        this(Lists.newArrayList());
    }

    public WeightedRoundRule(List<WeightedSender> weightedSenders) {
        configWeightedSenders = Maps.newHashMap();
        weightedSenders.forEach(weightedSender -> {
            configWeightedSenders.put(weightedSender.ipAddress, weightedSender);
        });
    }


    @Override
    Sender choose(LoadBalancer lb, Object key) {

        ReferenceDef referenceDef = new ReferenceDef();
        RuleObject ruleObject = (RuleObject) key;
        referenceDef.setService(ruleObject.getService());
        List<Sender> reachableServers = Lists.newArrayList(lb.getRouter().getUpSenders(referenceDef));

        int total = 0;
        WeightedSender best = null;
        Sender rst = null;
        for (Sender sender : reachableServers) {
            WeightedSender weightedSender = configWeightedSenders.get(sender.getIpAddress());
            if (weightedSender == null) {
                logger.warn("can't find WeightedSender by provider " + sender.getIpAddress());
                continue;
            }
            // 每次获取请求按权重提升当前权重
            weightedSender.currentWeight += weightedSender.effectiveWeight;
            // 为保持平衡，将所有提升的权重累加，在选出结果后扣除该权重
            total += weightedSender.effectiveWeight;
            // 有效权重自适应
            if (weightedSender.effectiveWeight < weightedSender.weight) {
                weightedSender.effectiveWeight++;
            }
            // 选出当前权重因子最大的作为最终结果
            if (best == null || weightedSender.currentWeight > best.currentWeight) {
                best = weightedSender;
                rst = sender;
            }
        }
        if (best == null) {
            return null;
        }

        // 选出的结果后从扣除本次循环所有负载累加的权重，把这个负载推到队列的最后方
        best.currentWeight -= total;
        return rst;
    }

    @Override
    public String getName() {
        return "weightedRound";
    }


    class WeightedSender {
        public int weight;
        public int currentWeight;
        public int effectiveWeight;
        public IpAddress ipAddress;
    }
}
